package com.shiro.realm;

import com.common.Const;
import com.okok.entity.User;
import com.shiro.entity.Token;
import com.shiro.token.StatelessToken;
import com.util.HmacSHA256Utils;
import com.shiro.services.AccountSimpleService;
import com.shiro.services.AccountStatelessService;
import com.shiro.entity.ShiroUser;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;


/**
 * 移动端Realm
 * @author Eason
 */
public class StatelessRealm extends AuthorizingRealm {
	
	private static Logger logger = LoggerFactory.getLogger(StatelessRealm.class);
	
	protected AccountStatelessService accountStatelessService;
	@Autowired
	public void setAccountStatelessService(AccountStatelessService accountStatelessService) {
		this.accountStatelessService = accountStatelessService;
	}
	
	protected AccountSimpleService accountSimpleService;
	@Autowired
	public void setAccountSimpleService(AccountSimpleService accountSimpleService) {
		this.accountSimpleService = accountSimpleService;
	}
	
	/**
	 * 设置认证支持的token类型
	 * 逻辑：
	 * 1、stateless只能使用StatelessToken认证
	 */
	@Override
    public boolean supports(AuthenticationToken token) {
		logger.info("[StatelessRealm] supports setting");
		// 仅支持StatelessToken类型的Token执行doGetAuthenticationInfo来认证
        return token instanceof StatelessToken;
    }
    
	/**
	 * 认证回调函数,登录时调用.
	 */
	@Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authToken) throws AuthenticationException {
		logger.info("[StatelessRealm]认证");
		// 1、获取(sequence、token、params、clientDigest)
		StatelessToken statelessToken = (StatelessToken) authToken;
        
        // 2、根据token字符串获取Token对象
        String username = statelessToken.getUsername();
        Token token = accountStatelessService.findUserByToken(username);
        if (null == token){
        	throw new UnknownAccountException();
        }

        // 3、生成密钥(与前端生成密钥的方法相同)
        String key =  token.getSecret() + Const._DASH + statelessToken.getSequence();
        
        // 4、在服务器端生成客户端参数消息摘要
        String serverDigest = HmacSHA256Utils.digest(key, statelessToken.getParams());
        logger.info("client digest: " + statelessToken.getClientDigest());
        logger.info("server digest: " + serverDigest);
        
        // 5、从token中获取User,然后进行客户端消息摘要和服务器端消息摘要的匹配,
        User user = token.getUser();
        
        if (user.getDelflag()) {
        	throw new UnknownAccountException();
		}
        

        return new SimpleAuthenticationInfo(
        		new ShiroUser(
        				user.getId(), 
        				user.getLoginName(),
        				user.getName()),
                serverDigest,
                getName()
           );
    }
	
	/**
	 * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.
	 */
	@Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		logger.info("[StatelessRealm]鉴权");
		
		ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
		User user = accountSimpleService.findUserByLoginName(shiroUser.loginName, "rate");
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		if (user==null) {
			return info;
		}
		info.addRoles(user.getRoleList());
		info.addStringPermissions(user.getPermissions());
		return info;
    }
	
}